#include "StdAfx.h"
#include <windows.h>
#include <snapi.h>

#include "Theme.h"
#include "Titlebar.h"
#include "GraphicFunctions.h"
#include "CommonWM.h"

#ifdef _DEBUG
#include "crtdbg.h"
#endif

// Registry key locations & flags
// also see snapi.h

#define SN_RINGMODE_ROOT        HKEY_CURRENT_USER
#define SN_RINGMODE_PATH        TEXT("ControlPanel\\Notifications\\ShellOverrides")
#define SN_RINGMODE_VALUE       TEXT("Mode")

#define SN_RINGMODE_SOUND       0
#define SN_RINGMODE_VIBRATE     1
#define SN_RINGMODE_MUTE        2

#define BATTERYLEVEL_VERYLOW    0
#define BATTERYLEVEL_LOW       21
#define BATTERYLEVEL_MEDIUM    41
#define BATTERYLEVEL_HIGH      61
#define BATTERYLEVEL_VERYHIGH  81

#define BATTERY_STATE_NORMAL     0
#define BATTERY_STATE_NOTPRESENT 1
#define BATTERY_STATE_CHARGING   2
#define BATTERY_STATE_LOW        4
#define BATTERY_STATE_CRITICAL   8

// copied from WM6 snapi.h
////////////////////////////////////////////////////////////////////////////////
// BluetoothStatePowerOn
// Gets a value indicating whether Bluetooth is powered on
#define SN_BLUETOOTHSTATEPOWERON_ROOT HKEY_LOCAL_MACHINE
#define SN_BLUETOOTHSTATEPOWERON_PATH TEXT("System\\State\\Hardware")
#define SN_BLUETOOTHSTATEPOWERON_VALUE TEXT("Bluetooth")
#define SN_BLUETOOTHSTATEPOWERON_BITMASK 1

// copied from WM6 snapi.h
////////////////////////////////////////////////////////////////////////////////
// BluetoothStateA2DPConnected
// Gets a value indicating whether Bluetooth A2DP is connected
#define SN_BLUETOOTHSTATEA2DPCONNECTED_ROOT HKEY_LOCAL_MACHINE
#define SN_BLUETOOTHSTATEA2DPCONNECTED_PATH TEXT("System\\State\\Hardware")
#define SN_BLUETOOTHSTATEA2DPCONNECTED_VALUE TEXT("Bluetooth")
#define SN_BLUETOOTHSTATEA2DPCONNECTED_BITMASK 4

// copied from WM6 snapi.h
////////////////////////////////////////////////////////////////////////////////
// WiFiStatePowerOn
// Gets a value indicating whether Wi-Fi is powered on
#define SN_WIFISTATEPOWERON_ROOT HKEY_LOCAL_MACHINE
#define SN_WIFISTATEPOWERON_PATH TEXT("System\\State\\Hardware")
#define SN_WIFISTATEPOWERON_VALUE TEXT("WiFi")
#define SN_WIFISTATEPOWERON_BITMASK 2

// copied from WM6 snapi.h
////////////////////////////////////////////////////////////////////////////////
// CellNetwork Connected
// Gets a value indicating what cellular system is used for connection 
#define SN_CELLSYSTEMCONNECTED_ROOT HKEY_LOCAL_MACHINE
#define SN_CELLSYSTEMCONNECTED_PATH TEXT("System\\State\\Phone")
#define SN_CELLSYSTEMCONNECTED_VALUE TEXT("Cellular System Connected")
#define SN_CELLSYSTEMCONNECTED_GPRS_BITMASK     1
#define SN_CELLSYSTEMCONNECTED_1XRTT_BITMASK    2
#define SN_CELLSYSTEMCONNECTED_1XEVDO_BITMASK   4
#define SN_CELLSYSTEMCONNECTED_EDGE_BITMASK     8
#define SN_CELLSYSTEMCONNECTED_UMTS_BITMASK     16
#define SN_CELLSYSTEMCONNECTED_EVDV_BITMASK     32
#define SN_CELLSYSTEMCONNECTED_HSDPA_BITMASK    64
#define SN_CELLSYSTEMCONNECTED_CSD_BITMASK      2147483648 

HREGNOTIFY Titlebar::arrNotify[tbdCount] = {0};
int Titlebar::nBattery = 0; //0-100
int Titlebar::nBars = 0; //0-5
TCHAR Titlebar::szCarrier[TB_CARRIER_LENGTH] = {0};
TCHAR Titlebar::szTime[TB_TIME_LENGTH] = {0};

bool Titlebar::bCharging = false;
bool Titlebar::bBluetooth = false;
bool Titlebar::bSpeakerOn = false;
bool Titlebar::bVibrate = false;
bool Titlebar::bWifi = false;
bool Titlebar::bLocked = false;
bool Titlebar::bAlarm = false;
bool Titlebar::bA2dp = false;
bool Titlebar::bConnection = false;

void Titlebar::Initialize(HWND hWnd)
{
  HRESULT hr;
  
  for (int i = 0; i < tbdCount; i++)
    arrNotify[i] = NULL;

  int notifyIdx = 0;
  // Signal Strength
  hr = RegistryNotifyWindow(
        SN_PHONESIGNALSTRENGTH_ROOT,
        SN_PHONESIGNALSTRENGTH_PATH,
        SN_PHONESIGNALSTRENGTH_VALUE, 
        hWnd, NM_TITLEBAR, tbdSignal,
        NULL, &arrNotify[notifyIdx]);
  notifyIdx++;

  // Time
  hr = RegistryNotifyWindow(
      SN_TIME_ROOT,
      SN_TIME_PATH,
      SN_TIME_VALUE,
      hWnd, NM_TITLEBAR, tbdTime,
      NULL, &arrNotify[notifyIdx]);
  notifyIdx++;

  // Battery
  hr = RegistryNotifyWindow(
      SN_POWERBATTERYSTRENGTH_ROOT, 
      SN_POWERBATTERYSTRENGTH_PATH, 
      SN_POWERBATTERYSTRENGTH_VALUE,
      hWnd, NM_TITLEBAR, tbdBattery,
      NULL, &arrNotify[notifyIdx]);
  notifyIdx++;

  // Bluetooth
  hr = RegistryNotifyWindow(
      SN_BLUETOOTHSTATEPOWERON_ROOT,
      SN_BLUETOOTHSTATEPOWERON_PATH,
      SN_BLUETOOTHSTATEPOWERON_VALUE,
      hWnd, NM_TITLEBAR, tbdBluetooth,
      NULL, &arrNotify[notifyIdx]);
  notifyIdx++;

  // Speaker / Volume
  hr = RegistryNotifyWindow(
      SN_RINGMODE_ROOT,
      SN_RINGMODE_PATH,  
      SN_RINGMODE_VALUE,
      hWnd, NM_TITLEBAR, tbdVolume,
      NULL, &arrNotify[notifyIdx]);
  notifyIdx++;

  // WiFi
  hr = RegistryNotifyWindow(
      SN_WIFISTATEPOWERON_ROOT,
      SN_WIFISTATEPOWERON_PATH,
      SN_WIFISTATEPOWERON_VALUE,
      hWnd, NM_TITLEBAR, tbdConnections,
      NULL, &arrNotify[notifyIdx]);
  notifyIdx++;

  // Connections
  hr = RegistryNotifyWindow(
      SN_CONNECTIONSCOUNT_ROOT,
      SN_CONNECTIONSCOUNT_PATH,
      SN_CONNECTIONSCOUNT_VALUE,
      hWnd, NM_TITLEBAR, tbdConnections,
      NULL, &arrNotify[notifyIdx]);
  notifyIdx++;

  Refresh(0xFF);
}

void Titlebar::Terminate()
{
  for (int i = 0; i < tbdCount; i++)
  {
    if (arrNotify[i] != NULL)
    {
      RegistryCloseNotification(arrNotify[i]);
      arrNotify[i] = NULL;
    }
  }
}

void Titlebar::Refresh(UINT uWhich)
{
  DWORD dw;
  HRESULT hr;

#ifdef DEBUG_SCREENSHOTS
    StringCchPrintf(szTime, 12, TEXT("12:38"));
    StringCchPrintf(szCarrier, 50, TEXT("AT&T"));
    nBars = 3;
    nBattery = 13;
    bVibrate = false;
    bSpeakerOn = true;
    bBluetooth = true;
    bA2dp = false;
    bWifi = false;
    bConnection = true;
    return;
#endif

  // Signal Strength
  if (uWhich & tbdSignal)
  {
      hr = RegistryGetDWORD(
          SN_PHONESIGNALSTRENGTH_ROOT, 
          SN_PHONESIGNALSTRENGTH_PATH, 
          SN_PHONESIGNALSTRENGTH_VALUE,
          &dw);
      if (SUCCEEDED(hr))
        nBars = (int)((double)dw / 20.0);
  }

  // Operator name
  if (uWhich & tbdOperator)
  {
      hr = RegistryGetString(
          SN_PHONEOPERATORNAME_ROOT, 
          SN_PHONEOPERATORNAME_PATH, 
          SN_PHONEOPERATORNAME_VALUE, 
          szCarrier, sizeof(szCarrier));
  }

  // Time
  if (uWhich & tbdTime)
  {
      ::GetTimeFormat(LOCALE_USER_DEFAULT, 
          TIME_NOSECONDS, NULL, NULL, szTime, 12);
  }

  // Battery level & charging state
  // http://msdn.microsoft.com/en-us/library/aa456240.aspx
  if (uWhich & tbdBattery)
  {
    // get actual battery status
    SYSTEM_POWER_STATUS_EX pwrStatus = {0};
    if (GetSystemPowerStatusEx(&pwrStatus, FALSE))
    {
      nBattery = pwrStatus.BatteryLifePercent;
      if (nBattery == BATTERY_PERCENTAGE_UNKNOWN)
        nBattery = 1;
      bCharging = (pwrStatus.BatteryFlag & BATTERY_FLAG_CHARGING) != 0;
    } 
    else
    { // keep old way too
      hr = RegistryGetDWORD(
            SN_POWERBATTERYSTRENGTH_ROOT, 
            SN_POWERBATTERYSTRENGTH_PATH, 
            SN_POWERBATTERYSTRENGTH_VALUE,
            &dw);
      if (SUCCEEDED(hr))
      {
        nBattery = (dw & SN_POWERBATTERYSTRENGTH_BITMASK) >> 16;
        // convert it to full % value
        nBattery = (nBattery / 4) * 5;
        bCharging = (dw & BATTERY_STATE_CHARGING) != 0;
      }
    }
  }

  // Bluetooth on/off
  if (uWhich & tbdBluetooth)
  {
    hr = RegistryGetDWORD(
          SN_BLUETOOTHSTATEPOWERON_ROOT,
          SN_BLUETOOTHSTATEPOWERON_PATH,
          SN_BLUETOOTHSTATEPOWERON_VALUE,
          &dw);
    if (SUCCEEDED(hr))
    {
      bBluetooth = (dw & SN_BLUETOOTHSTATEPOWERON_BITMASK) != 0;

      // this is not needed, since this is the same RegKey as SN_BLUETOOTH*
      //RegistryGetDWORD(
      //    SN_BLUETOOTHSTATEA2DPCONNECTED_ROOT,
      //    SN_BLUETOOTHSTATEA2DPCONNECTED_PATH,
      //    SN_BLUETOOTHSTATEA2DPCONNECTED_VALUE,
      //    &dw);
      bA2dp = (dw & SN_BLUETOOTHSTATEA2DPCONNECTED_BITMASK) != 0;
    }
  }

  // Speaker / volume
  if (uWhich & tbdVolume)
  {
    hr = RegistryGetDWORD(
      SN_RINGMODE_ROOT,
      SN_RINGMODE_PATH,  
      SN_RINGMODE_VALUE,
      &dw);
    if (SUCCEEDED(hr))
    {
      bSpeakerOn = dw == SN_RINGMODE_SOUND;
      bVibrate = dw == SN_RINGMODE_VIBRATE;
    }
  }

  // Connections & Wifi
  if (uWhich & tbdConnections)
  {
    hr = RegistryGetDWORD(
        SN_WIFISTATEPOWERON_ROOT,
        SN_WIFISTATEPOWERON_PATH,
        SN_WIFISTATEPOWERON_VALUE,
        &dw);

    if (SUCCEEDED(hr))
      bWifi = (dw & SN_WIFISTATEPOWERON_BITMASK) != 0;

    hr = RegistryGetDWORD(
        SN_CONNECTIONSCOUNT_ROOT,
        SN_CONNECTIONSCOUNT_PATH,
        SN_CONNECTIONSCOUNT_VALUE,
        &dw);

    if (SUCCEEDED(hr))
      bConnection = dw != 0;
}

  if (uWhich & tbdOther)
  {
    bLocked = false; // TODO?
    bAlarm = false; // TODO?
  }
}

void Titlebar::Draw(HDC hdc, RECT rTitlebar)
{
  RECT rc;
  HGDIOBJ hOld;

  SetTextColor(hdc, Theme::rgbTitlebarText);
  SetBkColor(hdc, Theme::rgbTitlebarBackground);
  SetBkMode(hdc, TRANSPARENT);

  hOld = SelectObject(hdc, Theme::TitlebarFont);

  // Fill in background
  FillRect(hdc, &rTitlebar, Theme::hbrTitlebarBackground);

  // LEFT SIDE
  // Bars
  rc.left = rTitlebar.left;
  rc.bottom = rTitlebar.bottom - (Theme::TitlebarHeight - Theme::TitlebarFontSize) / 2;
  for (int i = 0; i < 5; i++) {
      rc.right = rc.left + 3;
      if (nBars > i)
        rc.top = rc.bottom - ((i + 1) * Theme::TitlebarFontSize) / 5;
      else
        rc.top = rc.bottom - 1;
      FillRect(hdc, &rc, Theme::hbrTitlebarSignal);
      rc.left = rc.right + 1;
  }

  // Carrier
  rc.top = rTitlebar.top + (Theme::TitlebarHeight - Theme::TitlebarFontSize) / 2;
  rc.left = rc.right + 2;
  // make proper clipping
  SIZE sz;
  GetTextExtentPoint(hdc, szTime, _tcslen(szTime), &sz);
  rc.right = rTitlebar.right / 2 - sz.cx / 2;
  SetTextAlign(hdc, TA_LEFT);

  DrawText(hdc, szCarrier, -1, &rc, DT_LEFT | DT_TOP | DT_NOPREFIX);

  // Time
  rc.left = rTitlebar.left;
  rc.right = rTitlebar.right;
  DrawText(hdc, szTime, -1, &rc, DT_CENTER | DT_TOP | DT_NOCLIP);

  // Battery
  int x = rTitlebar.right - Theme::TitlebarSkinBATTERY_WIDTH;
  int y = rTitlebar.top;
  BitBlt(hdc, x, y, Theme::TitlebarSkinBATTERY_WIDTH, Theme::TitlebarHeight, 
    Theme::hdcSkin, Theme::TitlebarSkinBATTERY_X_OFFSET, 0, SRCCOPY);

  if (nBattery) {
      rc.top = rTitlebar.top + Theme::TitlebarSkinBATTERY_BAR_OFFSETY;
      rc.bottom = rc.top + Theme::TitlebarSkinBATTERY_BAR_HEIGHT;
      rc.left = x + Theme::TitlebarSkinBATTERY_BAR_OFFSETX;
      rc.right = rc.left + (nBattery * Theme::TitlebarSkinBATTERY_BAR_WIDTH) / 100;
      FillRect(hdc, &rc, Theme::hbrTitlebarBattery);
  }

  // Speaker
  if (bVibrate) {
    x -= Theme::TitlebarSkinVIBRATE_WIDTH + Theme::TitlebarSkinICON_SPACING;
    BitBlt(hdc, x, y, Theme::TitlebarSkinVIBRATE_WIDTH, Theme::TitlebarHeight, 
      Theme::hdcSkin, Theme::TitlebarSkinVIBRATE_X_OFFSET, 0, SRCCOPY);
  }
  else if (bSpeakerOn) {
    x -= Theme::TitlebarSkinSPEAKER_ON_WIDTH + Theme::TitlebarSkinICON_SPACING;
    BitBlt(hdc, x, y, Theme::TitlebarSkinSPEAKER_ON_WIDTH, Theme::TitlebarHeight, 
      Theme::hdcSkin, Theme::TitlebarSkinSPEAKER_ON_X_OFFSET, 0, SRCCOPY);
  }
  else {
    x -= Theme::TitlebarSkinSPEAKER_OFF_WIDTH + Theme::TitlebarSkinICON_SPACING;
    BitBlt(hdc, x, y, Theme::TitlebarSkinSPEAKER_OFF_WIDTH, Theme::TitlebarHeight, 
      Theme::hdcSkin, Theme::TitlebarSkinSPEAKER_OFF_X_OFFSET, 0, SRCCOPY);
  }

  // Bluetooth
  if (bBluetooth) {
    x -= Theme::TitlebarSkinBLUETOOTH_WIDTH + Theme::TitlebarSkinICON_SPACING;
    BitBlt(hdc, x, y, Theme::TitlebarSkinBLUETOOTH_WIDTH, Theme::TitlebarHeight, 
      Theme::hdcSkin, Theme::TitlebarSkinBLUETOOTH_X_OFFSET, 0, SRCCOPY);
  }
  if (bA2dp) {
    x -= Theme::TitlebarSkinA2DP_WIDTH + Theme::TitlebarSkinICON_SPACING;
    BitBlt(hdc, x, y, Theme::TitlebarSkinA2DP_WIDTH, Theme::TitlebarHeight,
      Theme::hdcSkin, Theme::TitlebarSkinA2DP_X_OFFSET, 0, SRCCOPY);
  }

  // WiFi Connection
  if (bWifi) {
    x -= Theme::TitlebarSkinWIFI_WIDTH + Theme::TitlebarSkinICON_SPACING;
    BitBlt(hdc, x, y, Theme::TitlebarSkinWIFI_WIDTH, Theme::TitlebarHeight, 
      Theme::hdcSkin, Theme::TitlebarSkinWIFI_X_OFFSET, 0, SRCCOPY);
  }

  // Data Connection
  if (bConnection) {
    x -= Theme::TitlebarSkinCONNECTION_WIDTH + Theme::TitlebarSkinICON_SPACING;
    BitBlt(hdc, x, y, Theme::TitlebarSkinCONNECTION_WIDTH, Theme::TitlebarHeight, 
      Theme::hdcSkin, Theme::TitlebarSkinCONNECTION_X_OFFSET, 0, SRCCOPY);
  }

  //cleanup
  SelectObject(hdc, hOld);
}